package cn.demoncat.util.sec;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.lang3.StringUtils;

import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.constant.StringConstant;
import cn.demoncat.util.lang.entity.SetStr;

/**
 * 描述：HMAC-SHA256（HmacSHA256） 消息摘要（数据唯一签名，不可逆）
 * <br/>	    
 * <br/>	    
 * MAC算法：结合MD5和SHA的优势，加入密钥元素，是更安全的消息摘要算法
 *
 * @author 延晓磊
 *
 * @since 2016年8月11日
 */
public class HmacSHA256Util {

	/**
	 * 加密算法
	 */
	public static final String ALGORITHM = "HmacSHA256";
	
	/**
	 * HmacSHA256 签名
	 * 
	 * @param data
	 * @param key
	 * 
	 * @return 数据签名 (64位16进制字符串)
	 * 
	 * @author 延晓磊
	 *
	 * @since 2016年8月11日
	 */
    public static String digest(byte[] data, String key) {
    	try {
    		// 消息认证算法
    		Mac mac = Mac.getInstance(ALGORITHM);
    		// 密钥
    		SecretKeySpec secretKey = new SecretKeySpec(StringUtil.getBytes(key), ALGORITHM);
    		// 初始化密钥
    		mac.init(secretKey);
    		// 加密，转换为16进制
    		return HexUtil.encode(mac.doFinal(data));
		} catch (Exception e) {
			throw new RuntimeException("HmacSHA256签名失败", e);
		}
    }
    
	/**
	 * HmacSHA256 签名
	 * 
	 * @param data
	 * @param key
	 * 
	 * @return 数据签名 (64位16进制字符串)
	 * 
	 * @author 延晓磊
	 *
	 * @since 2016年8月11日
	 */
    public static String digest(String data, String key) {
    	return digest(StringUtil.getBytes(data), key);
    }
    
	/**
	 * HMAC-SHA256签名算法
	 * 
	 * @param map  		参数：排除"sign"和"signature"，key的键为"key"
	 * @param key		密钥：将拼接在末尾，不能为空
	 * 
	 * @return 将键按ASCII排序(排除空值) -> 按URL格式拼接 -> HMAC-SHA256签名 -> 转换为大写
	 * 
	 * @author 延晓磊
	 *
	 * @since 2016年8月11日
	 */
	public static String sign(Map<String, String> map, String key) {
		return sign(map, null, null, key);
	}
	
	/**
	 * HMAC-SHA256签名算法
	 * 
	 * @param map  		参数
	 * @param ignores	排除的键：默认["sign","signature"]
	 * @param keyName	密钥的键名：默认"key"
	 * @param key		密钥：将拼接在末尾，不能为空
	 * 
	 * @return 将键按ASCII排序(排除空值) -> 按URL格式拼接 -> HMAC-SHA256签名 -> 转换为大写
	 * 
	 * @author 延晓磊
	 *
	 * @since 2016年8月11日
	 */
	public static String sign(Map<String, String> map, Set<String> ignores, String keyName, String key) {
		// 默认值
		if (ignores == null) {
			ignores = SetStr.getInstance("sign","signature");
		}
		if (StringUtils.isBlank(keyName)) {
			keyName = "key";
		}
		// 组装Map为List
		List<String> list = new ArrayList<>();
		for (Map.Entry<String, String> entry : map.entrySet()) {
			// 将非空Entry放入List
			if (StringUtils.isNotBlank(entry.getValue()) && !ignores.contains(entry.getKey())) {
				// k=v
				list.add(entry.getKey() + StringConstant.EQUAL + entry.getValue());
			}
		}
		// 排序List
		Collections.sort(list);
		// 拼接List元素，以&分隔
		String url = StringUtils.join(list, StringConstant.PARAM_SEPARATOR);
		// 拼接字符串的末尾添加KEY密钥
		url = url + StringConstant.PARAM_SEPARATOR +keyName+ StringConstant.EQUAL + key;
		// MD5签名，转换为大写
		return HmacSHA256Util.digest(url,key).toUpperCase();
	}
	
 
}
